package com.ljh.myrpc.Proxy;

import cn.hutool.core.collection.CollUtil;
import com.ljh.myrpc.RpcApplication;
import com.ljh.myrpc.config.RpcConfig;
import com.ljh.myrpc.constant.RpcConstant;
import com.ljh.myrpc.fault.retry.RetryStrategy;
import com.ljh.myrpc.fault.retry.RetryStrategyFactory;
import com.ljh.myrpc.fault.tolerant.TolerantStrategy;
import com.ljh.myrpc.fault.tolerant.TolerantStrategyFactory;
import com.ljh.myrpc.loadbalancer.LoadBalancer;
import com.ljh.myrpc.loadbalancer.LoadBalancerFactory;
import com.ljh.myrpc.model.RpcRequest;
import com.ljh.myrpc.model.RpcResponse;
import com.ljh.myrpc.model.ServiceMetaInfo;
import com.ljh.myrpc.registry.Registry;
import com.ljh.myrpc.registry.RegistryFactory;
import com.ljh.myrpc.server.tcp.VertxTcpClient;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 服务代理类(JDK动态代理)
 */
@Slf4j
public class ServiceProxy implements InvocationHandler {
    /**
     * 调用代理
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 指定序列化器
//        Serializer serializer = SerializerFactory.getInstance(RpcApplication.getRpcConfig().getSerializer());

        // 构造请求
        RpcRequest rpcRequest = RpcRequest.builder()
                .serviceName(method.getDeclaringClass().getName()) // 返回全限定名
                .methodName(method.getName())
                .parameterTypes(method.getParameterTypes())
                .args(args)
                .build();
        // 序列化
//        byte[] serialized = serializer.serialize(rpcRequest);
        // 发送HTTP请求
        // 从注册中心获取服务提供者地址
        RpcConfig rpcConfig = RpcApplication.getRpcConfig();
        Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());
        ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
        serviceMetaInfo.setServiceName(rpcRequest.getServiceName());
        serviceMetaInfo.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION);
        List<ServiceMetaInfo> serviceMetaInfos = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());
        if(CollUtil.isEmpty(serviceMetaInfos)){
            throw new RuntimeException("未找到服务提供者，请检查服务是否已注册");
        }
        // 获取客户端IP地址
        InetAddress localHost = InetAddress.getLocalHost();
        String hostAddress = localHost.getHostAddress();
        // 负载均衡策略
        LoadBalancer loadBalancer = LoadBalancerFactory.getInstance(rpcConfig.getLoadBalancer());
        // 将调用方法名作为负载均衡参数
        HashMap<String, Object> requestParams = new HashMap<>();
        requestParams.put("consumerIpAddress", hostAddress);
        ServiceMetaInfo serviceProvider = loadBalancer.select(requestParams, serviceMetaInfos);

//        // 发送请求
//        try (HttpResponse httpResponse = HttpRequest.post(serviceProvider.getServiceAddress())
//                     .body(serialized)
//                     .execute()){
//            byte[] result = httpResponse.bodyBytes();
//            // 反序列化响应
//            RpcResponse rpcResponse = serializer.deserialize(result, RpcResponse.class);
//            return rpcResponse.getData();
//        }catch (IOException e){
//            e.printStackTrace();
//        }
        // 发送TCP请求
        // 重试机制
        RpcResponse rpcResponse = null;
        try {
            RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(rpcConfig.getRetryStrategy());
            rpcResponse = retryStrategy.doRetry(() -> VertxTcpClient.doRequest(rpcRequest, serviceProvider));
        } catch (Exception e) {
            // 容错机制
            TolerantStrategy tolerantStrategy = TolerantStrategyFactory.getInstance(rpcConfig.getTolerantStrategy());
            // 构建数据
            Map<String , Object> context = new HashMap<>();
            context.put("rpcRequest", rpcRequest);
            context.put("serviceMetaInfoList", serviceMetaInfos);
            context.put("exception", e);
            rpcResponse = tolerantStrategy.doTolerant(context, e);
        }
        return rpcResponse.getData();
    }
}
